package org.hawk.gwt.ppc.reflect.lang;

import java.lang.reflect.Modifier;

import org.hawk.gwt.ppc.reflect.emul.java.lang.reflect.Constructor;
import org.hawk.gwt.ppc.reflect.emul.java.lang.reflect.Field;
import org.hawk.gwt.ppc.reflect.emul.java.lang.reflect.Method;

/**
 * This is common wrapper for all arrays in the system. Multidimensional arrays
 * are represented as "arrays of arrays".
 * 
 * @author alex.bereznevatiy@gmail.com
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class ArrayClassDelegate<T> extends ClassDelegate<T[]> {

	private ClassDelegate<?> componentType;

	ArrayClassDelegate(Class<T[]> arrayType, ClassDelegate<T> componentType) {
		super(arrayType);
		this.componentType = componentType;
	}

	public Class<T[]> toClass() {
		Class<T[]> result = super.toClass();
		if (result == null) {
			throw new NoClassDefFoundError(componentType.getName()+"[]");
		}
		return result;
	}

	@Override
	public T[] newInstance() throws InstantiationException,
			IllegalAccessException {
		throw new InstantiationException(
				"Array cannot be instantiated this way - use java.lang.reflect.Array class instead");
	}

	@Override
	public boolean isArray() {
		return true;
	}

	@Override
	public String getName() {
		String componentName = componentType.getName();
		if (componentName.indexOf('[') < 0) {
			return SystemClassLoader.getArrayTypeName(componentName);
		}
		return "[" + componentName;
	}

	@Override
	public ClassDelegate<? super T[]> getSuperclass() {
		return forClass(Object.class);
	}

	@Override
	public Package getPackage() {
		return componentType.getPackage();
	}

	@Override
	public ClassDelegate<?> getComponentType() {
		if (componentType.isArray()) {
			return componentType.getComponentType();
		}
		return componentType;
	}

	@Override
	public String getSimpleName() {
		return componentType.getSimpleName() + "[]";
	}

	@Override
	public String getCanonicalName() {
		if (componentType.getCanonicalName() == null) {
			return null;
		}
		return componentType.getCanonicalName() + "[]";
	}

	@Override
	public Field[] getFields() throws SecurityException {
		return new Field[0];
	}

	@Override
	public Constructor<T[]>[] getConstructors() throws SecurityException {
		return new Constructor[0];
	}

	@Override
	public boolean isAssignableFrom(Class cls) {
		return isAssignableFrom(forClass(cls));
	}

	@Override
	public boolean isAssignableFrom(ClassDelegate cls) {
		if (cls instanceof ArrayClassDelegate) {
			return componentType
					.isAssignableFrom(((ArrayClassDelegate<?>) cls).componentType);
		}
		return false;
	}

	@Override
	public boolean isInterface() {
		return false;
	}

	@Override
	public boolean isPrimitive() {
		return false;
	}

	@Override
	public boolean isAnnotation() {
		return false;
	}

	@Override
	public boolean isSynthetic() {
		return false;
	}

	@Override
	public ClassDelegate<?>[] getInterfaces() {
		return new ClassDelegate<?>[0];
	}

	@Override
	public int getModifiers() {
		int mod = componentType.getModifiers();
		return (mod & Modifier.PUBLIC) | (mod & Modifier.PRIVATE)
				| (mod & Modifier.PROTECTED) | Modifier.FINAL;
	}

	@Override
	public ClassDelegate<?> getDeclaringClass() {
		return null;
	}

	@Override
	public boolean isAnonymousClass() {
		return false;
	}

	@Override
	public boolean isLocalClass() {
		return false;
	}

	@Override
	public boolean isMemberClass() {
		return false;
	}

	@Override
	public Field[] getDeclaredFields() throws SecurityException {
		return new Field[0];
	}

	@Override
	public Method[] getDeclaredMethods() throws SecurityException {
		return new Method[0];
	}

	@Override
	public Constructor<T[]>[] getDeclaredConstructors()
			throws SecurityException {
		return new Constructor[0];
	}

	@Override
	public boolean isEnum() {
		return false;
	}

	@Override
	public T[][] getEnumConstants() {
		return null;
	}

	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		if (obj == this)
			return true;
		if (obj instanceof ArrayClassDelegate) {
			return ((ArrayClassDelegate<?>) obj).componentType.equals(componentType);
		}
		return false;
	}
}
